package com.showsu.editor.utils.aliyun;

import com.aliyun.oss.*;
import com.aliyun.oss.common.comm.Protocol;
import com.aliyun.oss.model.*;
import com.showsu.editor.utils.Constant;
import com.showsu.editor.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @Author: William
 * @Description: 阿里云oss工具类
 * @Date: Created in 4:31 下午 2023/4/12
 * @Modified By:
 */
@Slf4j
@Component
public class OssUtil {

    @Value("${aliyun.ossAccessLink}")
    private String ossAccessLink;

    @Value("${aliyun.ossEndpoint}")
    private String endpoint;

    @Value("${aliyun.smsAndOss.accessKeyId}")
    private String accessKeyId;

    @Value("${aliyun.smsAndOss.accessKeySecret}")
    private String accessKeySecret;


    public static final String imageDir = "img";

    public static final String handleWaterDir = "water";
    private static final String checkTextDir = "checkText";
    private static final String awemeRankingDir = "awemeRanking";
    private static final String fileDir = "file";
    //杭州oss的bucket
    private static final String bucketName = "showsu";
    //香港oss的bucket
    //private static final String bucketName = "showsuhk";

    private OSS ossClient;

    @PostConstruct
    public void init(){
        System.out.println("开始实例化 阿里云oss工具类");
        // 创建ClientBuilderConfiguration，它是OSSClient的配置类，可配置代理、连接超时、最大连接数等参数
        ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
        // 需要控制整个请求的时间,需要开启，默认不开启
        conf.setRequestTimeoutEnabled(true);
        // 需要控制整个请求的时间,设置请求超时，单位毫秒，默认值300秒
        conf.setRequestTimeout(30000);
        // 设置OSSClient允许打开的最大HTTP连接数，默认为1024个。
        conf.setMaxConnections(5120);
        // 设置Socket层传输数据的超时时间，默认为50000毫秒。
        conf.setSocketTimeout(30000);
        // 设置建立连接的超时时间，默认为50000毫秒。
        conf.setConnectionTimeout(5000);
        // 设置从连接池中获取连接的超时时间（单位：毫秒），默认不超时。
        conf.setConnectionRequestTimeout(-1);
        // 设置连接空闲超时时间。超时则关闭连接，默认为60000毫秒。
        conf.setIdleConnectionTime(10000);
        // 设置失败请求重试次数，默认为3次。
        conf.setMaxErrorRetry(3);
        // 设置是否支持将自定义域名作为Endpoint，默认支持。
        conf.setSupportCname(true);
        // 设置是否开启二级域名的访问方式，默认不开启。
        conf.setSLDEnabled(false);
        // 设置连接OSS所使用的协议（HTTP或HTTPS），默认为HTTP。
        conf.setProtocol(Protocol.HTTP);
//        // 设置用户代理，指HTTP的User-Agent头，默认为aliyun-sdk-java。
//        conf.setUserAgent("aliyun-sdk-java");
//        // 设置代理服务器IP，请将"<yourProxyHost>"替换为代理服务器的IP地址（如"196.128.xxx.xxx"）。
//        conf.setProxyHost("<yourProxyHost>");
//        // 设置代理服务器验证的用户名，请将"<yourProxyUserName>"替换为代理服务器的用户名（如"root"）。
//        conf.setProxyUsername("<yourProxyUserName>");
//        // 设置代理服务器验证的密码，请将"<yourProxyPassword>"替换为该用户的验证密码。
//        conf.setProxyPassword("<yourProxyPassword>");
        // 设置是否开启HTTP重定向，默认开启。
        conf.setRedirectEnable(true);
        // 设置是否开启SSL证书校验，默认开启。
        conf.setVerifySSLEnable(true);
        ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret, conf);
        System.out.println("结束实例化 阿里云oss工具类");
    }

    @PreDestroy
    public void destroy() {
        ossClient.shutdown();
        System.out.println("阿里云oss工具类 被销毁");
    }

    /**
     * 将网络上获取到的heic图片上传到oss，将oss地址返回，
     * 通过oss的格式转换功能（https://help.aliyun.com/document_detail/44703.html?spm=a2c4g.54464.0.i1），将jpg返回给前端
     * 这样做的原因是小程序端目前无法显示heic图片
     * @param imageUrl
     * @return
     */
    public String changeHeic2AliyunOssJpg(String imageUrl) {
        if (StringUtils.isBlank(imageUrl)) {
            return null;
        }
        int endPlace = imageUrl.indexOf(Constant.heicPicSuffix);
        if (endPlace > -1) {
            //获取文件名
            String beforeEndPlace = imageUrl.substring(0, endPlace);
            String fileName = imageUrl.substring(beforeEndPlace.lastIndexOf("/") + 1, endPlace + Constant.heicPicSuffix.length());
            String ossFileUrl = uploadImageUrlFile2AliyunOss(imageUrl, fileName);
            if (StringUtils.isNotBlank(ossFileUrl)) {
                return ossFileUrl + Constant.aliyunOssPicSuffix;
            }
        }
        return imageUrl;
    }

    private InputStream getInputStream(String imageUrl) throws IOException {;
        return this.getInputStream(imageUrl,30000);
    }

    private InputStream getInputStream(String imageUrl,int readTimeout) throws IOException {
        return this.getInputStream(imageUrl,5000,readTimeout);
    }

    private InputStream getInputStream(String imageUrl,int connectTimeout,int readTimeout) throws IOException {
        URL url = new URL(imageUrl);
        URLConnection connection = url.openConnection();
        // 设置连接超时时间为5秒
        connection.setConnectTimeout(connectTimeout);
        // 设置读取超时时间为30秒
        connection.setReadTimeout(readTimeout);
        InputStream inputStream =connection.getInputStream();
        return inputStream;
    }

    /**
     * 上传网络图片到阿里云oss
     * @param imageUrl
     * @return
     */
    public String uploadImageUrlFile2AliyunOss(String imageUrl, String fileName) {
        StringBuilder objectName = new StringBuilder(imageDir).append("/")
                .append(DateUtil.formatDate2yyyyMMdd(new Date())).append("/").append(fileName);
        InputStream inputStream = null;
        try {
            inputStream = this.getInputStream(imageUrl);
            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName.toString(), inputStream);
            // 设置该属性可以返回response。如果不设置，则返回的response为空。
            putObjectRequest.setProcess("true");
            // 创建PutObject请求。
            long start = System.currentTimeMillis();
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            log.info("OssUtil.uploadImageUrlFile2AliyunOss(String,String)图片上传结果:{}-响应耗时:{}", result, System.currentTimeMillis() - start);
            //如果是用内网oss上传的，返回的结果也是内网地址，要处理一下
            if (result != null && result.getResponse() != null && StringUtils.isNotBlank(result.getResponse().getUri())) {
                return result.getResponse().getUri().replace("-internal.", ".");
            }
        } catch (Exception e) {
            String errorInfo = String.format("OssUtil.uploadImageUrlFile2AliyunOss(String,String) error, imageUrl:%s, fileName:%s", imageUrl, fileName);
            log.error(errorInfo, e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("OssUtil.uploadImageUrlFile2AliyunOss(String,String) close inputStream error", e);
                }
            }
        }
        return null;
    }

    /**
     * @return
     */
    public String uploadFile2AliyunOss(File file, String fileName) {
        StringBuilder objectName = new StringBuilder(fileDir).append("/")
                .append(DateUtil.formatDate2yyyyMMdd(new Date())).append("/").append(fileName);
        try {
            // 创建PutObjectRequest对象
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName.toString(), file);
            // 上传文件
            long start = System.currentTimeMillis();
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            log.info("OssUtil.uploadFile2AliyunOss(File,String)文件上传结果:{}-响应耗时:{}", result, System.currentTimeMillis() - start);
            return ossAccessLink + objectName;
        } catch (Exception e) {
            String errorInfo = String.format("OssUtil.uploadFile2AliyunOss(File,String) error, fileName:%s", fileName);
            log.error(errorInfo, e);
        }
        return null;
    }

    /**
     * 上传去水印后的资源到阿里云oss
     * @param imageUrl
     * @return
     */
    public String uploadHandleWaterDirFile2AliyunOss(String imageUrl, String fileName) {
        if (StringUtils.isBlank(imageUrl) || StringUtils.isBlank(fileName)) {
            return null;
        }
        StringBuilder objectName = new StringBuilder(handleWaterDir).append("/")
                .append(DateUtil.formatDate2yyyyMMdd(new Date())).append("/").append(fileName);
        try {
            InputStream inputStream =null;
            try{
                inputStream=this.getInputStream(imageUrl);
                // 创建PutObjectRequest对象。
                PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName.toString(), inputStream);
                // 设置该属性可以返回response。如果不设置，则返回的response为空。
                putObjectRequest.setProcess("true");
                // 创建PutObject请求。
                long start = System.currentTimeMillis();
                PutObjectResult result = ossClient.putObject(putObjectRequest);
                log.info("OssUtil.uploadHandleWaterDirFile2AliyunOss(String,String)上传结果:{}-响应耗时:{}", result, System.currentTimeMillis() - start);
                //如果是用内网oss上传的，返回的结果也是内网地址，要处理一下
                if (result != null && result.getResponse() != null && StringUtils.isNotBlank(result.getResponse().getUri())) {
                    return result.getResponse().getUri().replace("-internal.", ".");
                }
            }catch (Exception e){
                String errorInfo = String.format("OssUtil.uploadHandleWaterDirFile2AliyunOss(String,String) error0, imageUrl:%s, fileName:%s", imageUrl, fileName);
                log.error(errorInfo, e);
            }finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        log.error("OssUtil.uploadImageUrlFile2AliyunOss(String,String) close inputStream error", e);
                    }
                }
            }
        } catch (Exception e) {
            String errorInfo = String.format("OssUtil.uploadHandleWaterDirFile2AliyunOss(String,String) error1, imageUrl:%s, fileName:%s", imageUrl, fileName);
            log.error(errorInfo, e);
        }
        return null;
    }

    /**
     * 上传去水印后的资源到阿里云oss
     * @param inputStream
     * @return
     */
    public String uploadHandleWaterDirFile2AliyunOss(InputStream inputStream, String fileName) {
        return this.uploadFile2AliyunOss(inputStream,handleWaterDir,fileName);
    }

    /**
     * 上传去违禁词的资源到阿里云oss
     * @param inputStream
     * @return
     */
    public String uploadCheckTextDirFile2AliyunOss(InputStream inputStream, String fileName) {
        return this.uploadFile2AliyunOss(inputStream,checkTextDir,fileName);
    }

    /**
     * 上传去违禁词的资源到阿里云oss
     * @param inputStream
     * @return
     */
    public String uploadXhsEditorDirFile2AliyunOss(InputStream inputStream, String fileName) {
        return this.uploadFile2AliyunOss(inputStream,Constant.xhsEditorDir,fileName);
    }

    /**
     * 上传资源到阿里云oss
     * @param inputStream
     * @return
     */
    public String uploadFile2AliyunOss(InputStream inputStream, String dir, String fileName) {
        StringBuilder objectName = new StringBuilder(dir).append("/")
                .append(DateUtil.formatDate2yyyyMMdd(new Date())).append("/").append(fileName);
        try {
            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName.toString(), inputStream);
            // 设置该属性可以返回response。如果不设置，则返回的response为空。
            putObjectRequest.setProcess("true");
            // 创建PutObject请求。
            long start = System.currentTimeMillis();
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            log.info("OssUtil.uploadFile2AliyunOss(InputStream,String,String),dir:{},fileName:{},上传结果:{}-响应耗时:{}", dir, fileName, result, System.currentTimeMillis() - start);
            //如果是用内网oss上传的，返回的结果也是内网地址，要处理一下
            if (result != null && result.getResponse() != null && StringUtils.isNotBlank(result.getResponse().getUri())) {
                return result.getResponse().getUri().replace("-internal.", ".");
            }
        } catch (Exception e) {
            String errorInfo = String.format("OssUtil.uploadFile2AliyunOss(InputStream,String,String) error, fileName:%s", fileName);
            log.error(errorInfo, e);
        }
        return null;
    }

    /**
     * 上传网络图片到阿里云oss
     * @param imageUrl
     * @param fileFullPath
     * @return
     */
    public String uploadFile2AliyunOss(String imageUrl, String fileFullPath) {
        if (StringUtils.isBlank(imageUrl) || StringUtils.isBlank(fileFullPath)) {
            return null;
        }
        try {
            InputStream inputStream = this.getInputStream(imageUrl, 10000, 30000);
            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileFullPath, inputStream);
            // 设置该属性可以返回response。如果不设置，则返回的response为空。
            putObjectRequest.setProcess("true");
            // 创建PutObject请求。
            long start = System.currentTimeMillis();
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            log.info("OssUtil.uploadFile2AliyunOss(String,String),imageUrl:{},fileFullPath:{},上传结果:{}-响应耗时:{}",
                    imageUrl, fileFullPath, result, System.currentTimeMillis() - start);
            //如果是用内网oss上传的，返回的结果也是内网地址，要处理一下
            if (result != null && result.getResponse() != null && StringUtils.isNotBlank(result.getResponse().getUri())) {
                return result.getResponse().getUri().replace("-internal.", ".");
            }
        } catch (Exception e) {
            String errorInfo = String.format("OssUtil.uploadFile2AliyunOss(String,String) error,imageUrl:%s,fileFullPath:%s", imageUrl, fileFullPath);
            log.error(errorInfo, e);
        }
        return null;
    }

    public String getOSSInternalUrl(String url){
        if(url.contains("oss-cn-hangzhou.aliyuncs.com")){
            return url.replace("oss-cn-hangzhou.aliyuncs.com", "oss-cn-hangzhou-internal.aliyuncs.com");
        }
        return null;
    }

    public String getOSSInternetUrl(String url){
        if(url.contains("oss-cn-hangzhou-internal.aliyuncs.com")){
            return url.replace("oss-cn-hangzhou-internal.aliyuncs.com", "oss-cn-hangzhou.aliyuncs.com");
        }
        return null;
    }

    public Boolean deleteXhsEditorDirFile(String fileUrl){
        return this.deleteDirFile(Constant.xhsEditorDir, fileUrl);
    }

    public Boolean deleteCheckTextDirFile(String fileUrl){
        return this.deleteDirFile(checkTextDir, fileUrl);
    }

    public Boolean deleteDirFile(String dir,String fileUrl){
        if(StringUtils.isBlank(dir)){
            return false;
        }
        if(StringUtils.isBlank(fileUrl)){
            return false;
        }
        String objectName=StringUtils.substringAfter(fileUrl,dir);
        if(StringUtils.isBlank(objectName)){
            return false;
        }
        // 创建OSSClient实例。
//        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        try {
            objectName=dir+objectName;
            // 删除文件或目录。如果要删除目录，目录必须为空。
            ossClient.deleteObject(bucketName, objectName);
            return true;
        } catch (Exception e) {
            String errorInfo = String.format("OssUtil.deleteCheckTextDirFile(String) error, fileUrl:%s", fileUrl);
            log.error(errorInfo, e);
        }
        return false;
    }

    /**
     * 清理某个前缀下的所有文件，若传入lastModifyTime则清理文件最后修改时间在此之前的文件
     * @param prefix 前缀
     * @param lastModifyTime 文件最后修改时间点
     */
    public Integer deleteObjectByPrefix(String prefix, Date lastModifyTime) {
        Integer counts = 0;
        try {
            // 列举所有包含指定前缀的文件并删除。
            String nextMarker = null;
            ObjectListing objectListing = null;
            do {
                ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName)
                        .withMaxKeys(1000)
                        .withPrefix(prefix)
                        .withMarker(nextMarker);

                objectListing = ossClient.listObjects(listObjectsRequest);
                if (objectListing.getObjectSummaries().size() > 0) {
                    List<String> keys = new ArrayList<String>();
                    for (OSSObjectSummary s : objectListing.getObjectSummaries()) {
                        if (lastModifyTime == null || s.getLastModified().getTime() < lastModifyTime.getTime()) {
                            keys.add(s.getKey());
                        }
                    }
                    DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName).withKeys(keys).withEncodingType("url");
                    DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(deleteObjectsRequest);
                    List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
                    counts += deletedObjects.size();
                    try {
                        for(String obj : deletedObjects) {
                            String deleteObj =  URLDecoder.decode(obj, "UTF-8");
                            log.info("OssUtil.deleteObjectByPrefix(String)清理oss文件:{}", deleteObj);
                        }
                    } catch (UnsupportedEncodingException uee) {
                        log.error("OssUtil.deleteObjectByPrefix(String) URLDecoder error", uee);
                    }
                }
                nextMarker = objectListing.getNextMarker();
            } while (objectListing.isTruncated());
        } catch (Exception e) {
            log.error("OssUtil.deleteObjectByPrefix(String) error", e);
        }
        return counts;
    }

}
